GdkWin32: Fix mouse events in presence of transparent windows on the desktop
authorLuca Bacci <luca.bacci982@gmail.com>
Fri, 6 Nov 2020 21:43:45 +0000 (22:43 +0100)
committerLuca Bacci <luca.bacci982@gmail.com>
Wed, 11 Nov 2020 11:46:52 +0000 (12:46 +0100)
In gdkdevice-win32.c we are interested in knowing which window
receives mouse input at a specific location.

Only WindowFromPoint is the right API for the task, other API's
(such as (Real)ChildWindowFromPoint(Ex)) have shortcomings because
they are really designed for other purposes. For example, only
WindowFromPoint is able to look through transparent layered windows.

So even if we want to find a direct child we have to use
WindowFromPoint and then walk up the hierarchy.

Fixes: #370, #417
See: !2800

gdk/win32/gdkdevice-win32.c

index 90fae48ac15ec957d9657a87a59057551df84264..35802b93a2eaafba9c25b1c03ee5b8e01409d168 100644 (file)
@@ -174,38 +174,25 @@ _gdk_device_win32_surface_at_position (GdkDevice       *device,
   GdkSurface *window = NULL;
   GdkWin32Surface *impl = NULL;
   POINT screen_pt, client_pt;
-  HWND hwnd, hwndc;
+  HWND hwnd;
   RECT rect;
 
-  GetCursorPos (&screen_pt);
-
-  /* Only consider visible children of the desktop to avoid the various
-   * non-visible windows you often find on a running Windows box. These
-   * might overlap our windows and cause our walk to fail. As we assume
-   * WindowFromPoint() can find our windows, we follow similar logic
-   * here, and ignore invisible and disabled windows.
-   */
-  hwnd = GetDesktopWindow ();
-  do {
-    window = gdk_win32_handle_table_lookup (hwnd);
-
-    if (window != NULL)
-      break;
-
-    screen_to_client (hwnd, screen_pt, &client_pt);
-    hwndc = ChildWindowFromPointEx (hwnd, client_pt, CWP_SKIPDISABLED  |
-                                                    CWP_SKIPINVISIBLE);
-
-    /* Verify that we're really inside the client area of the window */
-    if (hwndc != hwnd)
-      {
-       GetClientRect (hwndc, &rect);
-       screen_to_client (hwndc, screen_pt, &client_pt);
-       if (!PtInRect (&rect, client_pt))
-         hwndc = hwnd;
-      }
-
-  } while (hwndc != hwnd && (hwnd = hwndc, 1));
+  if (!GetCursorPos (&screen_pt))
+    return NULL;
+
+  /* Use WindowFromPoint instead of ChildWindowFromPoint(Ex).
+  *  Only WindowFromPoint is able to look through transparent
+  *  layered windows.
+  */
+  hwnd = GetAncestor (WindowFromPoint (screen_pt), GA_ROOT);
+
+  /* Verify that we're really inside the client area of the window */
+  GetClientRect (hwnd, &rect);
+  screen_to_client (hwnd, screen_pt, &client_pt);
+  if (!PtInRect (&rect, client_pt))
+    hwnd = NULL;
+
+  window = gdk_win32_handle_table_lookup (hwnd);
 
   if (window && (win_x || win_y))
     {